#include "cominc/drv_st7789.h"
#include <string.h>

/* 外部导入方法 */
extern void LCD_RST_WriteGPIO(uint8_t isSet);
extern void LCD_DC_WriteGPIO(uint8_t isSet);
extern void LCD_CS_WriteGPIO(uint8_t isSet);
extern void LCD_BLK_WriteGPIO(uint8_t isSet);
extern void LCD_Delay(uint32_t ms);
extern void LCD_WRByte(uint8_t byte);
extern uint32_t LCD_GetTick(void);
extern void LCD_WrArray(uint8_t *data, size_t dataLen);

static uint32_t g_frmStartTick;
static uint32_t g_frmCostTick;

uint16_t BACK_COLOR = WHITE, FORE_COLOR = BLACK;

#define LCD_SIZE 240
uint16_t GRAM[LCD_SIZE][LCD_SIZE];

static void LcdSt7789WrCmd(const uint8_t cmd)
{
    LCD_DC_WriteGPIO(GPIO_CLR);
    LCD_CS_WriteGPIO(GPIO_CLR);
    LCD_WRByte(cmd);
    LCD_CS_WriteGPIO(GPIO_SET);
}

static void LcdSt7789WrData8bit(const uint8_t data)
{
    LCD_DC_WriteGPIO(GPIO_SET);
    LCD_CS_WriteGPIO(GPIO_CLR);
    LCD_WRByte(data);
    LCD_CS_WriteGPIO(GPIO_SET);
}

static void LcdSt7789WrData16bit(const uint16_t da)
{
    LCD_DC_WriteGPIO(GPIO_SET);
    LCD_CS_WriteGPIO(GPIO_CLR);
    LCD_WRByte(da >> 8);
    LCD_WRByte(da);
    LCD_CS_WriteGPIO(GPIO_SET);
}

void LcdSt7789Init(struct LcdSt7789* lcd)
{
    LCD_RST_WriteGPIO(GPIO_CLR); // 复位
    LCD_Delay(50);
    LCD_RST_WriteGPIO(GPIO_SET);
    LCD_Delay(50);

    LcdSt7789WrCmd(0x11);
    LCD_Delay(10);
    /* Memory Data Access Control */
    LcdSt7789WrCmd(0x36);
    switch (lcd->rotate) {
        case LcdRotate180:  LcdSt7789WrData8bit(0xc0); break;
        case LcdRotate270:  LcdSt7789WrData8bit(0xa0); break;
        case LcdRotate0:    LcdSt7789WrData8bit(0x00); break;
        case LcdRotate90:   LcdSt7789WrData8bit(0x60); break;
    }
    /* RGB 5-6-5-bit  */
    LcdSt7789WrCmd(0x3A);
    LcdSt7789WrData8bit(0x05);
    /* Porch Setting */
    LcdSt7789WrCmd(0xB2);
    LcdSt7789WrData8bit(0x0C);
    LcdSt7789WrData8bit(0x0C);
    LcdSt7789WrData8bit(0x00);
    LcdSt7789WrData8bit(0x33);
    LcdSt7789WrData8bit(0x33);
    /*  Gate Control */
    LcdSt7789WrCmd(0xB7);
    LcdSt7789WrData8bit(0x35);
    /* VCOM Setting */
    LcdSt7789WrCmd(0xBB);
    LcdSt7789WrData8bit(0x35);
    /* LCM Control */
    LcdSt7789WrCmd(0xC0);
    LcdSt7789WrData8bit(0x2C);
    /* VDV and VRH Command Enable */
    LcdSt7789WrCmd(0xC2);
    LcdSt7789WrData8bit(0x01);
    /* VRH Set */
    LcdSt7789WrCmd(0xC3);
    LcdSt7789WrData8bit(0x13);
    /* VDV Set */
    LcdSt7789WrCmd(0xC4);
    LcdSt7789WrData8bit(0x20);
    /* Frame Rate Control in Normal Mode */
    LcdSt7789WrCmd(0xC6);
    LcdSt7789WrData8bit(0x0F);
    /* Power Control 1 */
    LcdSt7789WrCmd(0xD0);
    LcdSt7789WrData8bit(0xA4);
    LcdSt7789WrData8bit(0xA1);
    /* D6 Reg */
    LcdSt7789WrCmd(0xD6);
    LcdSt7789WrData8bit(0xA1);
    /* Positive Voltage Gamma Control */
    LcdSt7789WrCmd(0xE0);
    LcdSt7789WrData8bit(0xF0);
    LcdSt7789WrData8bit(0x00);
    LcdSt7789WrData8bit(0x04);
    LcdSt7789WrData8bit(0x04);
    LcdSt7789WrData8bit(0x04);
    LcdSt7789WrData8bit(0x05);
    LcdSt7789WrData8bit(0x29);
    LcdSt7789WrData8bit(0x33);
    LcdSt7789WrData8bit(0x3E);
    LcdSt7789WrData8bit(0x38);
    LcdSt7789WrData8bit(0x12);
    LcdSt7789WrData8bit(0x12);
    LcdSt7789WrData8bit(0x28);
    LcdSt7789WrData8bit(0x30);
    /* Negative Voltage Gamma Control */
    LcdSt7789WrCmd(0xE1);
    LcdSt7789WrData8bit(0xF0);
    LcdSt7789WrData8bit(0x07);
    LcdSt7789WrData8bit(0x0A);
    LcdSt7789WrData8bit(0x0D);
    LcdSt7789WrData8bit(0x0B);
    LcdSt7789WrData8bit(0x07);
    LcdSt7789WrData8bit(0x28);
    LcdSt7789WrData8bit(0x33);
    LcdSt7789WrData8bit(0x3E);
    LcdSt7789WrData8bit(0x36);
    LcdSt7789WrData8bit(0x14);
    LcdSt7789WrData8bit(0x14);
    LcdSt7789WrData8bit(0x29);
    LcdSt7789WrData8bit(0x32);
    /* Display Inversion On */
    LcdSt7789WrCmd(0x21);
    /* Sleep Out */
    LcdSt7789WrCmd(0x11);
    /* wait for power stability */
    LCD_Delay(120);
    /* display on */
    LcdSt7789WrCmd(0x29);
    LCD_BLK_WriteGPIO(GPIO_SET);

    /* clear back color */
    LcdSt7789Clear(lcd, lcd->colorBack);
}

void LcdSt7789AddressConfig(const struct LcdSt7789* lcd, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
{
    switch (lcd->rotate) {
        case LcdRotate180:
        case LcdRotate270:
        case LcdRotate0:    LcdSt7789WrData8bit(0x00); break;
        case LcdRotate90:   LcdSt7789WrData8bit(0x60); break;
    }
    LcdSt7789WrCmd(0x2a);
    LcdSt7789WrData16bit(x1);
    LcdSt7789WrData16bit(x2);

    LcdSt7789WrCmd(0x2b);
    LcdSt7789WrData16bit(y1);
    LcdSt7789WrData16bit(y2);

    LcdSt7789WrCmd(0x2C);
}

void LcdSt7789Clear(const struct LcdSt7789* lcd, uint16_t color)
{
    LcdSt7789AddressConfig(lcd, 0, 0, lcd->lcdWidth - 1, lcd->lcdHeight - 1);
    for (int i = 0; i < lcd->lcdWidth; i++) {
        for (int j = 0; j < lcd->lcdHeight; j++) {
            LcdSt7789WrData16bit(color);
        }
    }
}

/**
 * display a point on the lcd.
 *
 * @param   x   x position
 * @param   y   y position
 *
 * @return  void
 */
void LCD_DrawRadomPoint(const struct LcdSt7789* lcd, int x, int y, uint16_t color)
{
    LcdSt7789AddressConfig(lcd, x, y, x, y);
    LcdSt7789WrData16bit(color);
}

void LCD_DrawGramPoint(int x, int y, uint16_t color)
{
    GRAM[LCD_SIZE - x][y] = color;
}

void LCD_ClrGram()
{
    memset(GRAM, BACK_COLOR, sizeof(GRAM));
}

void LCD_SyncGram()
{
    g_frmStartTick = LCD_GetTick();
    for (size_t x = 0; x < 240; x++) {
				LCD_DC_WriteGPIO(GPIO_SET);
        LCD_WrArray((uint8_t *)GRAM[x], sizeof(GRAM[x]));
    }
    g_frmCostTick = LCD_GetTick() - g_frmStartTick;
}

void LCD_InitGram(const struct LcdSt7789* lcd)
{
    LcdSt7789AddressConfig(lcd, 0, 0, 240 - 1, 240 - 1); // 设置显示范围
    LCD_ClrGram();
}

uint32_t LCD_GetFrameTime()
{
    return g_frmCostTick;
}

/**
 * full color on the lcd.
 *
 * @param   x_start     start of x position
 * @param   y_start     start of y position
 * @param   x_end       end of x position
 * @param   y_end       end of y position
 * @param   color       Fill color
 *
 * @return  void
 */
void lcd_fill(uint16_t x_start, uint16_t y_start, uint16_t x_end, uint16_t y_end, uint16_t color)
{
    // uint16_t i = 0, j = 0;
    // uint32_t size = 0, size_remain = 0;
    // uint8_t *fill_buf = NULL;

    // size = (x_end - x_start) * (y_end - y_start) * 2;

    // if (size > LCD_CLEAR_SEND_NUMBER)
    // {
    //     /* the number of remaining to be filled */
    //     size_remain = size - LCD_CLEAR_SEND_NUMBER;
    //     size = LCD_CLEAR_SEND_NUMBER;
    // }

    // lcd_address_set(x_start, y_start, x_end, y_end);

    // fill_buf = (uint8_t *)rt_malloc(size);
    // if (fill_buf)
    // {
    //     /* fast fill */
    //     while (1)
    //     {
    //         for (i = 0; i < size / 2; i++)
    //         {
    //             fill_buf[2 * i] = color >> 8;
    //             fill_buf[2 * i + 1] = color;
    //         }
    //         LCD_DC_WriteGPIO(GPIO_SET);
    //         rt_spi_send(spi_dev_lcd, fill_buf, size);

    //         /* Fill completed */
    //         if (size_remain == 0)
    //             break;

    //         /* calculate the number of fill next time */
    //         if (size_remain > LCD_CLEAR_SEND_NUMBER)
    //         {
    //             size_remain = size_remain - LCD_CLEAR_SEND_NUMBER;
    //         }
    //         else
    //         {
    //             size = size_remain;
    //             size_remain = 0;
    //         }
    //     }
    //     rt_free(fill_buf);
    // }
    // else
    // {
    //     for (i = y_start; i <= y_end; i++)
    //     {
    //         for (j = x_start; j <= x_end; j++)
    //             lcd_write_half_word(color);
    //     }
    // }
}

void LcdSt7789SyncRam(const struct LcdSt7789* lcd, int x1, int y1, int x2, int y2, uint16_t* pRam)
{
    LcdSt7789AddressConfig(lcd, x1, y1, x2, y2);
    int ramLen = (x2 - x1 + 1) * (y2 - y1 + 1);
    for (int idx = 0; idx < ramLen; idx++) {
        LcdSt7789WrData16bit(pRam[idx]);
    }
}
